// Copyright 2016-2017 Federico Bond <federicobond@gmail.com> Copyright 
// Copyright 2024 Dmitry Litovchenko <i@dlitovchenko.ru>

// This program is free software: you can redistribute it and/or modify it under the terms of the
// GNU General Public License as published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
// even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
// 
// You should have received a copy of the GNU General Public License along with this program. If
// not, see <http://www.gnu.org/licenses/>. Copied from https://github.com/ethereum/solidity/blob/60cea4d70012332adcfdcedd8b4f53f556405841/docs/grammar/SolidityParser.g4

// $antlr-format alignTrailingComments true, columnLimit 150, minEmptyLines 1, maxEmptyLinesToKeep 1, reflowComments false, useTab false
// $antlr-format allowShortRulesOnASingleLine false, allowShortBlocksOnASingleLine true, alignSemicolons hanging, alignColons hanging

lexer grammar SolidityLexer;

ReservedKeywords
    : 'after'
    | 'alias'
    | 'apply'
    | 'auto'
    | 'byte'
    | 'case'
    | 'copyof'
    | 'default'
    | 'define'
    | 'final'
    | 'implements'
    | 'in'
    | 'inline'
    | 'let'
    | 'macro'
    | 'match'
    | 'mutable'
    | 'null'
    | 'of'
    | 'partial'
    | 'promise'
    | 'reference'
    | 'relocatable'
    | 'sealed'
    | 'sizeof'
    | 'static'
    | 'supports'
    | 'switch'
    | 'typedef'
    | 'typeof'
    | 'var'
    ;

Abstract
    : 'abstract'
    ;

Address
    : 'address'
    ;

Anonymous
    : 'anonymous'
    ;

As
    : 'as'
    ;

Assembly
    : 'assembly' -> pushMode(AssemblyBlockMode)
    ;

Bool
    : 'bool'
    ;

Break
    : 'break'
    ;

Bytes
    : 'bytes'
    ;

Calldata
    : 'calldata'
    ;

Catch
    : 'catch'
    ;

Constant
    : 'constant'
    ;

Constructor
    : 'constructor'
    ;

Continue
    : 'continue'
    ;

Contract
    : 'contract'
    ;

Delete
    : 'delete'
    ;

Do
    : 'do'
    ;

Else
    : 'else'
    ;

Emit
    : 'emit'
    ;

Enum
    : 'enum'
    ;

Error
    : 'error'
    ; // not a real keyword

Event
    : 'event'
    ;

External
    : 'external'
    ;

Fallback
    : 'fallback'
    ;

FalseLiteral
    : 'false'
    ;

Fixed
    : 'fixed'
    | ('fixed' [1-9][0-9]* 'x' [1-9][0-9]*)
    ;

/**
 * Bytes types of fixed length.
 */
FixedBytes
    : 'bytes1'
    | 'bytes2'
    | 'bytes3'
    | 'bytes4'
    | 'bytes5'
    | 'bytes6'
    | 'bytes7'
    | 'bytes8'
    | 'bytes9'
    | 'bytes10'
    | 'bytes11'
    | 'bytes12'
    | 'bytes13'
    | 'bytes14'
    | 'bytes15'
    | 'bytes16'
    | 'bytes17'
    | 'bytes18'
    | 'bytes19'
    | 'bytes20'
    | 'bytes21'
    | 'bytes22'
    | 'bytes23'
    | 'bytes24'
    | 'bytes25'
    | 'bytes26'
    | 'bytes27'
    | 'bytes28'
    | 'bytes29'
    | 'bytes30'
    | 'bytes31'
    | 'bytes32'
    ;

For
    : 'for'
    ;

From
    : 'from'
    ; // not a real keyword

Function
    : 'function'
    ;

Global
    : 'global'
    ; // not a real keyword

Hex
    : 'hex'
    ;

If
    : 'if'
    ;

Immutable
    : 'immutable'
    ;

Import
    : 'import'
    ;

Indexed
    : 'indexed'
    ;

Interface
    : 'interface'
    ;

Internal
    : 'internal'
    ;

Is
    : 'is'
    ;

Library
    : 'library'
    ;

Mapping
    : 'mapping'
    ;

Memory
    : 'memory'
    ;

Modifier
    : 'modifier'
    ;

New
    : 'new'
    ;

/**
 * Unit denomination for numbers.
 */
SubDenomination
    : 'wei'
    | 'gwei'
    | 'ether'
    | 'seconds'
    | 'minutes'
    | 'hours'
    | 'days'
    | 'weeks'
    | 'years'
    ;

Override
    : 'override'
    ;

Payable
    : 'payable'
    ;

Pragma
    : 'pragma' -> pushMode(PragmaMode)
    ;

Private
    : 'private'
    ;

Public
    : 'public'
    ;

Pure
    : 'pure'
    ;

Receive
    : 'receive'
    ;

Return
    : 'return'
    ;

Returns
    : 'returns'
    ;

Revert
    : 'revert'
    ; // not a real keyword

/**
 * Sized signed integer types.
 * int is an alias of int256.
 */
SignedIntegerType
    : 'int'
    | 'int8'
    | 'int16'
    | 'int24'
    | 'int32'
    | 'int40'
    | 'int48'
    | 'int56'
    | 'int64'
    | 'int72'
    | 'int80'
    | 'int88'
    | 'int96'
    | 'int104'
    | 'int112'
    | 'int120'
    | 'int128'
    | 'int136'
    | 'int144'
    | 'int152'
    | 'int160'
    | 'int168'
    | 'int176'
    | 'int184'
    | 'int192'
    | 'int200'
    | 'int208'
    | 'int216'
    | 'int224'
    | 'int232'
    | 'int240'
    | 'int248'
    | 'int256'
    ;

Storage
    : 'storage'
    ;

String
    : 'string'
    ;

Struct
    : 'struct'
    ;

Transient
    : 'transient'
    ; // not a real keyword

TrueLiteral
    : 'true'
    ;

Try
    : 'try'
    ;

Type
    : 'type'
    ;

Ufixed
    : 'ufixed'
    | ('ufixed' [1-9][0-9]+ 'x' [1-9][0-9]+)
    ;

Unchecked
    : 'unchecked'
    ;

Unicode
    : 'unicode'
    ;

/**
 * Sized unsigned integer types.
 * uint is an alias of uint256.
 */
UnsignedIntegerType
    : 'uint'
    | 'uint8'
    | 'uint16'
    | 'uint24'
    | 'uint32'
    | 'uint40'
    | 'uint48'
    | 'uint56'
    | 'uint64'
    | 'uint72'
    | 'uint80'
    | 'uint88'
    | 'uint96'
    | 'uint104'
    | 'uint112'
    | 'uint120'
    | 'uint128'
    | 'uint136'
    | 'uint144'
    | 'uint152'
    | 'uint160'
    | 'uint168'
    | 'uint176'
    | 'uint184'
    | 'uint192'
    | 'uint200'
    | 'uint208'
    | 'uint216'
    | 'uint224'
    | 'uint232'
    | 'uint240'
    | 'uint248'
    | 'uint256'
    ;

Using
    : 'using'
    ;

View
    : 'view'
    ;

Virtual
    : 'virtual'
    ;

While
    : 'while'
    ;

LParen
    : '('
    ;

RParen
    : ')'
    ;

LBrack
    : '['
    ;

RBrack
    : ']'
    ;

LBrace
    : '{'
    ;

RBrace
    : '}'
    ;

Colon
    : ':'
    ;

Semicolon
    : ';'
    ;

Period
    : '.'
    ;

Conditional
    : '?'
    ;

DoubleArrow
    : '=>'
    ;

RightArrow
    : '->'
    ;

Assign
    : '='
    ;

AssignBitOr
    : '|='
    ;

AssignBitXor
    : '^='
    ;

AssignBitAnd
    : '&='
    ;

AssignShl
    : '<<='
    ;

AssignSar
    : '>>='
    ;

AssignShr
    : '>>>='
    ;

AssignAdd
    : '+='
    ;

AssignSub
    : '-='
    ;

AssignMul
    : '*='
    ;

AssignDiv
    : '/='
    ;

AssignMod
    : '%='
    ;

Comma
    : ','
    ;

Or
    : '||'
    ;

And
    : '&&'
    ;

BitOr
    : '|'
    ;

BitXor
    : '^'
    ;

BitAnd
    : '&'
    ;

Shl
    : '<<'
    ;

Sar
    : '>>'
    ;

Shr
    : '>>>'
    ;

Add
    : '+'
    ;

Sub
    : '-'
    ;

Mul
    : '*'
    ;

Div
    : '/'
    ;

Mod
    : '%'
    ;

Exp
    : '**'
    ;

Equal
    : '=='
    ;

NotEqual
    : '!='
    ;

LessThan
    : '<'
    ;

GreaterThan
    : '>'
    ;

LessThanOrEqual
    : '<='
    ;

GreaterThanOrEqual
    : '>='
    ;

Not
    : '!'
    ;

BitNot
    : '~'
    ;

Inc
    : '++'
    ;

Dec
    : '--'
    ;

//@doc:inline
DoubleQuote
    : '"'
    ;

//@doc:inline
SingleQuote
    : '\''
    ;

/**
 * A non-empty quoted string literal restricted to printable characters.
 */
NonEmptyStringLiteral
    : '"' DoubleQuotedStringCharacter+ '"'
    | '\'' SingleQuotedStringCharacter+ '\''
    ;

/**
 * An empty string literal
 */
EmptyStringLiteral
    : '"' '"'
    | '\'' '\''
    ;

// Note that this will also be used for Yul string literals.
//@doc:inline
fragment DoubleQuotedStringCharacter
    : DoubleQuotedPrintable
    | EscapeSequence
    ;

// Note that this will also be used for Yul string literals.
//@doc:inline
fragment SingleQuotedStringCharacter
    : SingleQuotedPrintable
    | EscapeSequence
    ;

/**
 * Any printable character except single quote or back slash.
 */
fragment SingleQuotedPrintable
    : [\u0020-\u0026\u0028-\u005B\u005D-\u007E]
    ;

/**
 * Any printable character except double quote or back slash.
 */
fragment DoubleQuotedPrintable
    : [\u0020-\u0021\u0023-\u005B\u005D-\u007E]
    ;

/**
  * Escape sequence.
  * Apart from common single character escape sequences, line breaks can be escaped
  * as well as four hex digit unicode escapes \\uXXXX and two digit hex escape sequences \\xXX are allowed.
  */
fragment EscapeSequence
    : '\\' (
        ['"\\nrt\n\r]
        | 'u' HexCharacter HexCharacter HexCharacter HexCharacter
        | 'x' HexCharacter HexCharacter
    )
    ;

/**
 * A single quoted string literal allowing arbitrary unicode characters.
 */
UnicodeStringLiteral
    : 'unicode' (
        ('"' DoubleQuotedUnicodeStringCharacter* '"')
        | ('\'' SingleQuotedUnicodeStringCharacter* '\'')
    )
    ;

//@doc:inline
fragment DoubleQuotedUnicodeStringCharacter
    : ~["\r\n\\]
    | EscapeSequence
    ;

//@doc:inline
fragment SingleQuotedUnicodeStringCharacter
    : ~['\r\n\\]
    | EscapeSequence
    ;

// Note that this will also be used for Yul hex string literals.
/**
 * Hex strings need to consist of an even number of hex digits that may be grouped using underscores.
 */
HexString
    : 'hex' (('"' EvenHexDigits? '"') | ('\'' EvenHexDigits? '\''))
    ;

/**
 * Hex numbers consist of a prefix and an arbitrary number of hex digits that may be delimited by underscores.
 */
HexNumber
    : '0' 'x' HexDigits
    ;

//@doc:inline
fragment HexDigits
    : HexCharacter ('_'? HexCharacter)*
    ;

//@doc:inline
fragment EvenHexDigits
    : HexCharacter HexCharacter ('_'? HexCharacter HexCharacter)*
    ;

//@doc:inline
fragment HexCharacter
    : [0-9A-Fa-f]
    ;

/**
 * Scanned but not used by any rule, i.e, disallowed.
 * solc parser considers number starting with '0', not immediately followed by '.' or 'x' as
 * octal, even if non octal digits '8' and '9' are present.
 */
OctalNumber
    : '0' DecimalDigits ('.' DecimalDigits)?
    ;

/**
 * A decimal number literal consists of decimal digits that may be delimited by underscores and
 * an optional positive or negative exponent.
 * If the digits contain a decimal point, the literal has fixed point type.
 */
DecimalNumber
    : (DecimalDigits | (DecimalDigits? '.' DecimalDigits)) ([eE] '-'? DecimalDigits)?
    ;

//@doc:inline
fragment DecimalDigits
    : [0-9] ('_'? [0-9])*
    ;

/**
 * This is needed to avoid successfully parsing a number followed by a string with no whitespace between.
 */
DecimalNumberFollowedByIdentifier
    : DecimalNumber Identifier
    ;

/**
 * An identifier in solidity has to start with a letter, a dollar-sign or an underscore and
 * may additionally contain numbers after the first symbol.
 */
Identifier
    : IdentifierStart IdentifierPart*
    ;

//@doc:inline
fragment IdentifierStart
    : [a-zA-Z$_]
    ;

//@doc:inline
fragment IdentifierPart
    : [a-zA-Z0-9$_]
    ;

WS
    : [ \t\r\n\u000C]+ -> skip
    ;

COMMENT
    : '/*' .*? '*/' -> channel(HIDDEN)
    ;

LINE_COMMENT
    : '//' ~[\r\n]* -> channel(HIDDEN)
    ;

mode AssemblyBlockMode;

//@doc:inline
AssemblyDialect
    : '"evmasm"'
    ;

AssemblyLBrace
    : '{' -> popMode, pushMode(YulMode)
    ;

AssemblyFlagString
    : '"' DoubleQuotedStringCharacter+ '"'
    ;

AssemblyBlockLParen
    : '('
    ;

AssemblyBlockRParen
    : ')'
    ;

AssemblyBlockComma
    : ','
    ;

AssemblyBlockWS
    : [ \t\r\n\u000C]+ -> skip
    ;

AssemblyBlockCOMMENT
    : '/*' .*? '*/' -> channel(HIDDEN)
    ;

AssemblyBlockLINE_COMMENT
    : '//' ~[\r\n]* -> channel(HIDDEN)
    ;

mode YulMode;

YulBreak
    : 'break'
    ;

YulCase
    : 'case'
    ;

YulContinue
    : 'continue'
    ;

YulDefault
    : 'default'
    ;

YulFalse
    : 'false'
    ;

YulFor
    : 'for'
    ;

YulFunction
    : 'function'
    ;

YulIf
    : 'if'
    ;

YulLeave
    : 'leave'
    ;

YulLet
    : 'let'
    ;

YulSwitch
    : 'switch'
    ;

YulTrue
    : 'true'
    ;

YulHex
    : 'hex'
    ;

/**
 * Builtin functions in the EVM Yul dialect.
 */
YulEVMBuiltin
    : 'stop'
    | 'add'
    | 'sub'
    | 'mul'
    | 'div'
    | 'sdiv'
    | 'mod'
    | 'smod'
    | 'exp'
    | 'not'
    | 'lt'
    | 'gt'
    | 'slt'
    | 'sgt'
    | 'eq'
    | 'iszero'
    | 'and'
    | 'or'
    | 'xor'
    | 'byte'
    | 'shl'
    | 'shr'
    | 'sar'
    | 'addmod'
    | 'mulmod'
    | 'signextend'
    | 'keccak256'
    | 'pop'
    | 'mload'
    | 'mstore'
    | 'mstore8'
    | 'sload'
    | 'sstore'
    | 'tload'
    | 'tstore'
    | 'msize'
    | 'gas'
    | 'address'
    | 'balance'
    | 'selfbalance'
    | 'caller'
    | 'callvalue'
    | 'calldataload'
    | 'calldatasize'
    | 'calldatacopy'
    | 'extcodesize'
    | 'extcodecopy'
    | 'returndatasize'
    | 'returndatacopy'
    | 'mcopy'
    | 'extcodehash'
    | 'create'
    | 'create2'
    | 'call'
    | 'callcode'
    | 'delegatecall'
    | 'staticcall'
    | 'return'
    | 'revert'
    | 'selfdestruct'
    | 'invalid'
    | 'log0'
    | 'log1'
    | 'log2'
    | 'log3'
    | 'log4'
    | 'chainid'
    | 'origin'
    | 'gasprice'
    | 'blockhash'
    | 'blobhash'
    | 'coinbase'
    | 'timestamp'
    | 'number'
    | 'difficulty'
    | 'prevrandao'
    | 'gaslimit'
    | 'basefee'
    | 'blobbasefee'
    ;

YulLBrace
    : '{' -> pushMode(YulMode)
    ;

YulRBrace
    : '}' -> popMode
    ;

YulLParen
    : '('
    ;

YulRParen
    : ')'
    ;

YulAssign
    : ':='
    ;

YulPeriod
    : '.'
    ;

YulComma
    : ','
    ;

YulArrow
    : '->'
    ;

/**
 * Yul identifiers consist of letters, dollar signs, underscores and numbers, but may not start with a number.
 * In inline assembly there cannot be dots in user-defined identifiers. Instead see yulPath for expressions
 * consisting of identifiers with dots.
 */
YulIdentifier
    : YulIdentifierStart YulIdentifierPart*
    ;

//@doc:inline
fragment YulIdentifierStart
    : [a-zA-Z$_]
    ;

//@doc:inline
fragment YulIdentifierPart
    : [a-zA-Z0-9$_]
    ;

/**
 * Hex literals in Yul consist of a prefix and one or more hexadecimal digits.
 */
YulHexNumber
    : '0' 'x' [0-9a-fA-F]+
    ;

/**
 * Decimal literals in Yul may be zero or any sequence of decimal digits without leading zeroes.
 */
YulDecimalNumber
    : '0'
    | ([1-9] [0-9]*)
    ;

/**
 * String literals in Yul consist of one or more double-quoted or single-quoted strings
 * that may contain escape sequences and printable characters except unescaped line breaks or
 * unescaped double-quotes or single-quotes, respectively.
 */
YulStringLiteral
    : '"' DoubleQuotedStringCharacter* '"'
    | '\'' SingleQuotedStringCharacter* '\''
    ;

//@doc:inline
YulHexStringLiteral
    : HexString
    ;

YulWS
    : [ \t\r\n\u000C]+ -> skip
    ;

YulCOMMENT
    : '/*' .*? '*/' -> channel(HIDDEN)
    ;

YulLINE_COMMENT
    : '//' ~[\r\n]* -> channel(HIDDEN)
    ;

mode PragmaMode;

/**
 * Pragma token. Can contain any kind of symbol except a semicolon.
 * Note that currently the solidity parser only allows a subset of this.
 */
//@doc:name pragma-token
//@doc:no-diagram
PragmaToken
    : ~[;]+
    ;

PragmaSemicolon
    : ';' -> popMode
    ;

PragmaWS
    : [ \t\r\n\u000C]+ -> skip
    ;

PragmaCOMMENT
    : '/*' .*? '*/' -> channel(HIDDEN)
    ;

PragmaLINE_COMMENT
    : '//' ~[\r\n]* -> channel(HIDDEN)
    ;